import { ESCesiumViewer } from "@czmSrc/ESCesiumViewer";
import { MultiPointsEditing } from "..";
import { extendClassProps, ReactivePropsToNativePropsAndChanged, Event, createNextAnimateFrameEvent, ObjResettingWithEvent, Destroyable, createProcessingFromAsyncFunc, step, track } from "xbsj-base";
import { ModifyEditingPoint } from "./ModifyEditingPoint";
import { ESJVector3DArray } from "earthsdk3";
import { TranslationEditing } from "../..";
import { getEllipsoidGeodesicCenter } from "@czmSrc/utils";

export class MultiPointsModifyEditing extends MultiPointsEditing {
    public _menuPoiCommand = this.disposeVar(new Event<[editingPoint: ModifyEditingPoint, command: string]>());
    menuPoiCommand(editingPoint: ModifyEditingPoint, command: string) {
        this._menuPoiCommand.emit(editingPoint, command);
    }

    private _forceModifyPosEditorEvent = this.disposeVar(new Event<[ModifyEditingPoint]>());
    modify(posEditor: ModifyEditingPoint) {
        this._forceModifyPosEditorEvent.emit(posEditor);
    }
    override resetPositions(value?: ESJVector3DArray): boolean {
        if (!super.resetPositions(value)) return true;
        const toAddItems = value ? value.map(e => new ModifyEditingPoint(this, e)) : [];
        this.editingPoints.splice(0, this.editingPoints.length, ...toAddItems);
        return true;
    }
    private _overEvent = this.disposeVar(new Event<[number]>());
    get overEvent() { return this._overEvent; }

    public translationEditing: TranslationEditing;// 

    constructor(czmViewer: ESCesiumViewer) {
        super(czmViewer);
        {
            // 通过第一个点，移动所有点
            const event = this.ad(createNextAnimateFrameEvent(
                this.moveWithFirstPositionChanged,
                this.editingPoints.changedEvent
            ))
            this.ad(new ObjResettingWithEvent(event, () => {
                if (!this.moveWithFirstPosition || this.editingPoints.length <= 1) return undefined;
                return new FirstPositionResetting(this.editingPoints.get(0), this);
            }))
        }
        const translationEditing = this.translationEditing = this.disposeVar(new TranslationEditing(czmViewer));
        translationEditing.enabled = false;
        this.currentProcess = this.disposeVar(createProcessingFromAsyncFunc(async cancelsManager => {
            cancelsManager.disposer.dispose(() => {
                (this.debug ?? MultiPointsModifyEditing.defaults.debug) && console.log(`---editing canceled~~~`);
                this.status = 'None';
                this.enabled = false;
            });
            let index = -1;
            do {
                // 进入Modifying修改状态
                (this.debug ?? MultiPointsModifyEditing.defaults.debug) && console.log(`进入Modifying修改状态`);
                this.status = 'Modifying';
                index = await cancelsManager.promise(step(cancelsManager, async cancelsManager => {
                    const d = cancelsManager.disposer;
                    const modifying = d.ad(new ModifyingEditingProcessing(this));
                    d.dispose(() => `modifying canceled!`);
                    // 当有位置点时，自动进入第一个位置点的修改状态
                    if (this.editingPoints.length > 0) {
                        modifying.modify(this.editingPoints.get(index === -1 || index >= this.editingPoints.length ? 0 : index));
                    }
                    d.dispose(this.cancelEvent.disposableOn(() => modifying.cancel()));
                    d.dispose(this._forceModifyPosEditorEvent.disposableOn(posEditor => modifying.modify(posEditor)));
                    const modifyingIndex = await cancelsManager.promise(new Promise<number>(resolve => {
                        d.dispose(modifying.overEvent.disposableOnce((number) => {
                            resolve(number)
                        }));
                    }));
                    return modifyingIndex;
                }));
                if (index === -1) {
                    (this.debug ?? MultiPointsModifyEditing.defaults.debug) && console.log(`退出编辑状态`);
                    break;
                } else {
                    if (this.editingPoints.length >= (this.maxPointsNum ?? MultiPointsModifyEditing.defaults.maxPointsNum)) {
                        return;
                    }
                    const l = this.editingPoints.length;
                    if (l === 0) {
                        console.error(`impl.positionEditors.length === 0!`);
                        return;
                    }
                    const loop = this.loop ?? MultiPointsModifyEditing.defaults.loop;
                    const p0 = this.editingPoints.get(index - 1).position;
                    if (!p0) return;
                    let nextIndex = (index) % this.editingPoints.length;
                    // loop && nextIndex == 0 && nextIndex++;
                    const pn = this.editingPoints.get(nextIndex);
                    const p1 = pn.position;
                    if (!p1) return;
                    const m = getEllipsoidGeodesicCenter(p0, p1);
                    if (!m) return;
                    if (m) m[2] += this.czmViewer.editingHeightOffset ?? 0;
                    let currentPosEditor = new ModifyEditingPoint(this, m);
                    this.editingPoints.splice(index, 0, currentPosEditor);
                }
            } while (true);
            this.status = 'None';
            this.enabled = false;
        }));
    }
}
export namespace MultiPointsModifyEditing {
    export const createDefaultProps = () => ({
        ...MultiPointsEditing.createDefaultProps(),
        moveWithFirstPosition: false,
    })
}
extendClassProps(MultiPointsModifyEditing.prototype, MultiPointsModifyEditing.createDefaultProps);
export interface MultiPointsModifyEditing extends ReactivePropsToNativePropsAndChanged<ReturnType<typeof MultiPointsModifyEditing.createDefaultProps>> { }

class FirstPositionResetting extends Destroyable {
    constructor(private _firstPositionEditor: ModifyEditingPoint, private multiPointsModifyEditing: MultiPointsModifyEditing) {
        super()
        this._firstPositionEditor.positionChanged.disposableOn((v, ov) => {
            if (v === undefined || ov === undefined) return;
            var d = [v[0] - ov[0], v[1] - ov[1], v[2] - ov[2]];
            const l = this.multiPointsModifyEditing.editingPoints.length
            for (let i = 1; i < l; i++) {
                const penN = this.multiPointsModifyEditing.editingPoints.get(i);
                const pp = penN.position;
                if (pp === undefined) continue;
                penN.position = [pp[0] + d[0], pp[1] + d[1], pp[2] + d[2]];
            }
        });
    }
}


export class ModifyingEditingProcessing extends Destroyable {
    // overEvent返回索引，表示要在此处增加元素，如果为-1，则表示直接结束
    private _overEvent = this.disposeVar(new Event<[number]>());
    get overEvent() { return this._overEvent; }

    private _cancelEvent = this.disposeVar(new Event());
    cancel() { this._cancelEvent.emit(); }

    private _forceModifyPosEditorEvent = this.disposeVar(new Event<[ModifyEditingPoint]>());
    modify(posEditor: ModifyEditingPoint) {
        this._forceModifyPosEditorEvent.emit(posEditor);
    }

    constructor(multiPointsModifyEditing: MultiPointsModifyEditing) {
        super();

        multiPointsModifyEditing.menuPoiEnabled = true;
        this.dispose(() => multiPointsModifyEditing.menuPoiEnabled = false);

        let currentPointModifying: PointModifying | undefined;
        const resetPointModifying = () => {
            currentPointModifying?.destroy();
            currentPointModifying = undefined;
            multiPointsModifyEditing.status = 'Modifying';
        };
        this.dispose(resetPointModifying);

        const modify = (posEditor: ModifyEditingPoint) => {
            resetPointModifying();
            multiPointsModifyEditing.status = 'Modifying_Point';
            currentPointModifying = new PointModifying(multiPointsModifyEditing, posEditor);
            currentPointModifying.dispose(currentPointModifying.overEvent.disposableOn(() => {
                resetPointModifying();
            }));
        };

        this.dispose(this._forceModifyPosEditorEvent.disposableOn(modify));

        this.dispose(multiPointsModifyEditing._menuPoiCommand.disposableOn((posEditor, commandName) => {
            if (commandName === 'delete') {
                if (multiPointsModifyEditing.editingPoints.length <= 2) return;
                const n = multiPointsModifyEditing.editingPoints.indexOf(posEditor);
                if (n === -1) {
                    console.error(`posEditor not found`);
                    throw new Error(`posEditor not found`);
                }
                multiPointsModifyEditing.editingPoints.splice(n, 1);
            } else if (commandName === 'modify') {
                modify(posEditor);
            } else if (commandName === 'add') {
                const n = multiPointsModifyEditing.editingPoints.indexOf(posEditor);
                if (n === -1) {
                    console.error(`posEditor not found`);
                    throw new Error(`posEditor not found`);
                }
                this._overEvent.emit(n + 1);
            } else if (commandName === 'dbClick') {
                this._overEvent.emit(-1);
            } else {
                alert(`未知的commandName(${commandName})`);
            }
        }));

        this.dispose(this._cancelEvent.disposableOn(() => {
            this._overEvent.emit(-1);
        }));
    }
}

export class PointModifying extends Destroyable {
    private _overEvent = this.disposeVar(new Event());
    get overEvent() { return this._overEvent; }

    constructor(private _modifying: MultiPointsModifyEditing, private _posEditor: ModifyEditingPoint) {
        super();
        this.dispose(this._posEditor.toDestroyEvent.disposableOn(() => {
            this._overEvent.emit();
        }));

        const { translationEditing } = this._modifying;
        translationEditing.enabled = true;
        translationEditing.heading = 0;
        this.dispose(() => { translationEditing.enabled = false; });

        if (!this._posEditor.position) {
            console.error(`!this._posEditor.position`);
            throw new Error(`!this._posEditor.position`);
        }
        translationEditing.position = this._posEditor.position;
        this.dispose(track([this._posEditor, 'position'], [translationEditing, 'position']));
        this.dispose(translationEditing.enabledChanged.disposableOnce(enabled => {
            if (enabled) return;
            this._overEvent.emit();
        }));
    }
}